/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.action;

import java.io.Serializable;

import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.loader.ListLoadResult;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;


/**
 *
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <B>
 *           the type of the beans returned in the {@link PagingLoadResult}
 * @param <C>
 *           the type of the given {@link PagingLoadConfig}
 * @param <D>
 *           the type of the RPC returned {@link PagingLoadResult}
 */
public abstract class DkBasePagingLoader<B extends Serializable, C extends PagingLoadConfig, D extends PagingLoadResult<B>> extends PagingLoader<C, D>
{
	private String loadingProgressMessage;
	private boolean showProgress;
	private final DkPagingToolbar<B, C, D> pagingToolbar;

	/**
	 * Constructor with showProgress = true
	 *
	 * @param store
	 *           the {@link ListStore} to be re-populated when received new {@link ListLoadResult}
	 * @param loadingProgressMessage
	 *           the loading progress message
	 * @param pageSize
	 *           the paging size
	 */
	public DkBasePagingLoader(final ListStore<B> store, final String loadingProgressMessage, final int pageSize)
	{
		this(store, loadingProgressMessage, pageSize, true);
	}

	/**
	 * Constructor
	 *
	 * @param store
	 *           the {@link ListStore} to be re-populated when received new {@link ListLoadResult}
	 * @param loadingProgressMessage
	 *           the loading progress message
	 * @param pageSize
	 *           the paging size
	 * @param showProgress
	 *           boolean indicating whether progress bar should be shown (or incremented if already shown) for this
	 *           action
	 */
	public DkBasePagingLoader(final ListStore<B> store, final String loadingProgressMessage, final int pageSize, final boolean showProgress)
	{
		super(new DkBasePagingLoaderRpcProxy<B, C, D>());
		this.loadingProgressMessage = loadingProgressMessage;
		this.showProgress = showProgress;
		addLoadHandler(new LoadResultListStoreBinding<C, B, D>(store));
		pagingToolbar = new DkPagingToolbar<B, C, D>(pageSize);
		pagingToolbar.bind(this);
	}

	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
	// LOADING OVERRIDDEN METHODS (to integrate action mechanism) AND ACTION STARTING METHODS
	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

	private boolean loadFromAction = false;

	/**
	 * Method initializing load mechanism called from run method of {@link DkPagingLoadAction}.
	 *
	 * @param callingAction
	 *           the calling action
	 */
	void loadFromAction(final DkPagingLoadAction<B, C, D> callingAction)
	{
		// At this time , the action is already started.
		// And because we can't have 2 actions started at the same time setting the action in the proxy is "thread" ( = action ) safe.
		loadFromAction = true;
		getProxy().setBasePagingLoadAction(callingAction);
		super.load();
	}

	/**
	 * Execute the loading action.
	 */
	void loadWithActionExecute()
	{
		final DkBaseAction<?> reloadAction = getReloadAction();
		reloadAction.execute(true); // will call loadFromAction(BasePagingLoadAction) if no action is already running.
	}

	/**
	 * Called from toolbar or from combobox via {@link #load(PagingLoadConfig)}
	 *
	 * @param loadConfig
	 *           the load config
	 */
	void loadWithActionExecute(final C loadConfig)
	{
		useLoadConfig(loadConfig);
		loadWithActionExecute();
	}

	/**
	 * Called from toolbar when there is no loadConfig
	 *
	 * @param limit
	 *           the paging limit
	 * @param offset
	 *           the paging offset
	 */
	void loadWithActionExecute(final int limit, final int offset)
	{
		setOffset(offset);
		setLimit(limit);
		loadWithActionExecute();
	}

	/**
	 * Method called from an action or directly in some cases (for example when remote sorting a grid), in this second
	 * case an action is started
	 */
	@Override
	public boolean load()
	{
		if (!loadFromAction)
		{
			loadWithActionExecute();
		}
		else
		{
			super.load();
		}
		return true;
	}

	/**
	 * Called in load process. But can be called directly for example by dynamic combobox, that's why we need to start an
	 * action in this case.
	 */
	@Override
	public boolean load(final C loadConfig)
	{
		boolean ret = false;
		pagingToolbar.setEnabled(true);// Handle the fact that sometimes due to network pb the BasePagingToolbar does not receive the onLoad event and stay disabled.
		if (!loadFromAction)
		{
			loadWithActionExecute(loadConfig);
		}
		else
		{
			ret = super.load(loadConfig);
		}
		return ret;
	}

	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
	// ACTUAL LOAD METHOD AND RETRUN PROCESSORS
	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

	/**
	 * Called by {@link DkPagingLoadAction} and call the actual loading method
	 *
	 * @param listLoadConfig
	 */
	void doLoad(final C listLoadConfig)
	{
		// Here the action is already started. This is called from action.
		loadFromAction = false;
		doLoad(listLoadConfig, new DkBaseAsyncCallback<D>(getProxy().getBasePagingLoadAction(), true)
		{
			@Override
			public void processSuccess(final D result)
			{
				DkBasePagingLoader.this.processLoadSuccess(result);
			}

			@Override
			public boolean processFailure(final Throwable caught)
			{
				return DkBasePagingLoader.this.processLoadFailure(caught);
			}
		});
	}

	/**
	 * Loading method that implement business logic.
	 *
	 * @param pagingLoadConfig
	 *           the load configuration
	 * @param callback
	 *           the {@link DkBaseAsyncCallback}
	 */
	protected abstract void doLoad(final C pagingLoadConfig, final DkBaseAsyncCallback<D> callback);

	/**
	 * Called by the Callback from BaseAsyncCallback.
	 *
	 * @see {@link DkBaseAsyncCallback#processSuccess(Object)}
	 * @param result
	 */
	protected void processLoadSuccess(final D result)
	{
		getProxy().getBasePagingLoadAction().getProxyAsyncCallback().onSuccess(result);
	}

	/**
	 * Called by the Callback from BaseAsyncCallback.
	 *
	 * @see {@link DkBaseAsyncCallback#processFailure(Throwable)}
	 * @param caught
	 * @return
	 */
	protected boolean processLoadFailure(final Throwable caught)
	{
		getProxy().getBasePagingLoadAction().getProxyAsyncCallback().onFailure(caught);
		return true;
	}

	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
	// GETTERS / SETTERS
	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

	@SuppressWarnings("unchecked")
	@Override
	public DkBasePagingLoaderRpcProxy<B, C, D> getProxy()
	{
		return (DkBasePagingLoaderRpcProxy<B, C, D>) super.getProxy();
	}

	/**
	 * Return a new BasePagingLoadAction.<br>
	 * This method is final , if you want to override, override {@link #getReloadAction(boolean, boolean)} instead.<br>
	 * To provide custom chain action, use getReloadChainAction()
	 *
	 * @return a new BasePagingLoadAction instance
	 */
	public final DkPagingLoadAction<B, C, D> getReloadAction()
	{
		return getReloadAction(false);
	}

	/**
	 * Return a new BasePagingLoadAction with showProgress set to this class attribute.<br>
	 * This method is final , if you want to override, override {@link #getReloadAction(boolean, boolean)} instead.<br>
	 * To provide custom chain action, use getReloadChainAction()
	 *
	 * @param resetOffset
	 *           to reset the config before loading
	 * @return a new BasePagingLoadAction instance
	 */
	public final DkPagingLoadAction<B, C, D> getReloadAction(final boolean resetOffset)
	{
		return getReloadAction(resetOffset, showProgress);
	}

	/**
	 * Return a new BasePagingLoadAction.<br>
	 * To provide custom chain action, use getReloadChainAction()
	 *
	 * @param resetOffset
	 *           to reset the config before loading
	 * @param showProgress
	 *           to show progress or not
	 * @return a new BasePagingLoadAction instance
	 */
	public DkPagingLoadAction<B, C, D> getReloadAction(final boolean resetOffset, final boolean showProgress)
	{
		final DkPagingLoadAction<B, C, D> ret = new DkPagingLoadAction<B, C, D>(loadingProgressMessage, showProgress, this, resetOffset);
		final DkBaseAction<?> reloadChainAction = getReloadChainAction();
		if (reloadChainAction != null)
		{
			ret.setLastChainAction(reloadChainAction);
		}
		return ret;
	}

	/**
	 * Override this method to provide chain action to be executed after each reload action.
	 *
	 * @return a {@link DkBaseAction} or null if no chain action is to be executed (default is null)
	 */
	protected DkBaseAction<?> getReloadChainAction()
	{
		return null;
	}

	/**
	 * @return the pagingToolbar
	 */
	public DkPagingToolbar<B, C, D> getPagingToolbar()
	{
		return pagingToolbar;
	}
}
